package utils

import (
	"errors"
	"server/global"
	"server/model/request"
	"time"

	"github.com/golang-jwt/jwt/v4"
)

type Jwt struct {
	AccessTokenSecret  []byte
	RefreshTokenSecret []byte
}

var (
	TokenExpired     = errors.New("token is expired")           // Token 已过期
	TokenNotValidYet = errors.New("token not active yet")       // Token 不可用
	TokenMalformed   = errors.New("that's not even a token")    // Token 格式错误
	TokenInvalid     = errors.New("couldn't handle this token") // Token 无效
)

// NewJwt 初始化jwt密钥实例
func NewJwt() *Jwt {
	return &Jwt{
		AccessTokenSecret:  []byte(global.Config.Jwt.AccessTokenSecret),
		RefreshTokenSecret: []byte(global.Config.Jwt.RefreshTokenSecret),
	}
}

// CreateAccessClaims create access token Claims
func (j *Jwt) CreateAccessClaims(baseClaims request.BaseClaims) request.JwtCustomAccessClaims {
	expireTime, _ := ParseDuration(global.Config.Jwt.AccessTokenExpiryTime)
	claims := request.JwtCustomAccessClaims{
		BaseClaims: baseClaims,
		RegisteredClaims: jwt.RegisteredClaims{
			Audience:  jwt.ClaimStrings{"GinBlog"},
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(expireTime)),
			Issuer:    global.Config.Jwt.Issuer,
		},
	}
	return claims
}

// CreateAccessToken create AccessToken for jwt
func (j *Jwt) CreateAccessToken(claim request.JwtCustomAccessClaims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)
	return token.SignedString(j.AccessTokenSecret)
}

// CreateRefreshClaims create refresh token Claims
func (j *Jwt) CreateRefreshClaims(baseClaims request.BaseClaims) request.JwtCustomRefreshClaims {
	expireTime, _ := ParseDuration(global.Config.Jwt.RefreshTokenExpiryTime)
	claims := request.JwtCustomRefreshClaims{
		UserID: baseClaims.UserID,
		RegisteredClaims: jwt.RegisteredClaims{
			Audience:  jwt.ClaimStrings{"GinBlog"},
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(expireTime)),
			Issuer:    global.Config.Jwt.Issuer,
		},
	}
	return claims
}

// CreateRefreshToken create RefreshToken for jwt
func (j *Jwt) CreateRefreshToken(claims request.JwtCustomRefreshClaims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(j.RefreshTokenSecret)
}

// ParseAccessToken Parse AccessToken and return claims
func (j *Jwt) ParseAccessToken(tokenString string) (*request.JwtCustomAccessClaims, error) {
	claim, err := j.parseToken(tokenString, &request.JwtCustomAccessClaims{}, j.AccessTokenSecret)
	if err != nil {
		return nil, err
	}
	// 断言类型为 JwtCustomAccessClaims
	if customClaims, ok := claim.(*request.JwtCustomAccessClaims); ok {
		return customClaims, nil
	}
	return nil, TokenInvalid
}

// ParseRefreshToken Parse RefreshToken and return claims
func (j *Jwt) ParseRefreshToken(tokenString string) (*request.JwtCustomRefreshClaims, error) {
	claim, err := j.parseToken(tokenString, &request.JwtCustomRefreshClaims{}, j.RefreshTokenSecret)
	if err != nil {
		return nil, err
	}
	// 断言类型为 JwtCustomRefreshClaims
	if customClaims, ok := claim.(*request.JwtCustomRefreshClaims); ok {
		return customClaims, nil
	}
	return nil, TokenInvalid
}

// parseToken 通用的 Token 解析方法，验证 Token 是否有效并返回 Claims
func (j *Jwt) parseToken(tokenString string, claim jwt.Claims, secretKey []byte) (interface{}, error) {
	token, err := jwt.ParseWithClaims(tokenString, claim, func(token *jwt.Token) (interface{}, error) {
		return secretKey, nil
	})
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			switch {
			case ve.Errors&jwt.ValidationErrorMalformed != 0:
				return nil, TokenMalformed
			case ve.Errors&jwt.ValidationErrorExpired != 0:
				return nil, TokenExpired
			case ve.Errors&jwt.ValidationErrorNotValidYet != 0:
				return nil, TokenNotValidYet
			default:
				return nil, TokenInvalid
			}
		}
		return nil, TokenInvalid // 默认返回token无效
	}
	// 如果token验证通过,返回claim
	if token.Valid {
		return token.Claims, nil
	}
	return nil, TokenInvalid
}
